﻿/*
 * Copyright 2011 Edward L. Stauff.                     Contact: <EdStauff@gmail.com>.
 * 
 * This file, "CustomAttributes.cs", is part of the "Stauffware.Common.Console" class 
 * library.  "Stauffware.Common" is free software: you can redistribute it and/or 
 * modify it under the terms of the GNU Lesser Public License as published by the 
 * Free Software Foundation, either version 3 of the License, or (at your option) 
 * any later version.
 * 
 * "Stauffware.Common" is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser Public License for more 
 * details.
 * 
 * You should have received a copy of the GNU Lesser Public License along with 
 * "Stauffware.Common".  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using System.Text;
using Stauffware.Common.Core;

namespace Stauffware.Common.Console
{
	//#################################################################################
	/// <summary>
	/// Use this custom attribute on a class containing fields or properties whose
	/// values can be set from the command line.
	/// </summary>

	[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
	public class CmdLineGroupAttribute : Attribute
	{
		/// <summary>
		/// Gets or sets the group name for command-line parameters associated with this class.
		/// If a group name is specified, then each argument name must be
		/// preceded on the command line with this group name and a period.
		/// </summary>
		/// <remarks>
		/// CmdLineParser will not accept multiple groups with the same non-null name.
		/// On the command line, group names can be abbreviated to any unambiguous prefix.
		/// </remarks>
		public string GroupName { get; set; }

		/// <summary>
		/// Gets or sets a help message for this group of parameters.
		/// If ResourceNamePrefix is non-null, then ResourceName+GroupHelp 
		/// is the name of the resource containing the help message.
		/// </summary>
		public string GroupHelp { get; set; }

		/// <summary>
		/// If non-null, then GroupName is a resource name, and CmdLineParser
		/// will prepend ResourceNamePrefix to GroupName before fetching the resource.
		/// GroupName also applies to the Help for all CmdLineArgAttributes
		/// on the members of CmdLineGroupAttribute's target class.
		/// </summary>
		public string ResourceNamePrefix 
		{
			get { return this._resourceNamePrefix; }
			set { this._resourceNamePrefix = value;			Assert.NotImplemented(); }
		}
		private string _resourceNamePrefix;

		/// <summary>
		/// Specifies whether CmdLineParser will automatically create command-line  
		/// parameters for public fields that are not tagged with CmdLineArgAttribute 
		/// or NotAvailableFromCmdLineAttribute. 
		/// </summary>
		public bool IncludePublicFieldsByDefault { get; set; }

		/// <summary>
		/// Specifies whether CmdLineParser will automatically create command-line  
		/// parameters for public properties that are not tagged with  
		/// CmdLineArgAttribute or NotAvailableFromCmdLineAttribute. 
		/// </summary>
		public bool IncludePublicPropertiesByDefault { get; set; }

		/// <summary>
		/// Marks a class as containing fields and/or properties whose values can be
		/// provided from the command-line.
		/// </summary>
		public CmdLineGroupAttribute ()
		{
		}

		/// <summary>
		/// Marks a class as containing fields and/or properties whose values can be
		/// provided from the command-line.
		/// </summary>
		/// <param name="groupName">
		///		Gets or sets the group name for command-line parameters associated with 
		///		this class.  If a group name is specified, then each argument name must
		///		be preceded on the command line with this group name and a period.
		/// </param>
		/// <param name="includePublicFields">
		///		Specifies whether CmdLineParser will automatically create command-line  
		///		parameters for public fields that are not tagged with CmdLineArgAttribute 
		///		or NotAvailableFromCmdLineAttribute. 
		/// </param>
		/// <param name="includePublicProps">
		///		Specifies whether CmdLineParser will automatically create command-line  
		///		parameters for public properties that are not tagged with  
		///		CmdLineArgAttribute or NotAvailableFromCmdLineAttribute. 
		/// </param>
		/// <param name="groupHelp">
		///		Gets or sets a help message (documentation) for this group of parameters.
		///		If ResourceNamePrefix is non-null, then ResourceName+GroupHelp 
		///		is the name of the resource containing the help message.
		/// </param>

		public CmdLineGroupAttribute (string groupName, bool includePublicFields, bool includePublicProps, string groupHelp)
		{
			this.GroupName = groupName;
			this.IncludePublicFieldsByDefault = includePublicFields;
			this.IncludePublicPropertiesByDefault = includePublicProps;
			this.GroupHelp = groupHelp;
		}
	}

	//#################################################################################
	/// <summary>
	/// Base class for CmdLineArgAttribute and NotAvailableFromCmdLineAttribute,
	/// which are mutually exclusive.
	/// </summary>

	[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property,
											AllowMultiple=false, Inherited=false)]
	public abstract class CmdLineMemberAttribute : Attribute
	{
		/// constructor
		protected CmdLineMemberAttribute ( ) { }
	}

	//#################################################################################
	/// <summary>
	/// Use this attribute to mark a field or property as being a command-line 
	/// parameter, and to specify command-line parameter options.
	/// </summary>

	public class CmdLineArgAttribute : CmdLineMemberAttribute
	{
		/// <summary>
		/// Gets or sets the name of the parameter, which must be unique within
		/// its CmdLineArgs parameter group.
		/// If the name begins with the switch character, it is considered a switch.
		/// If Name is not specified, the parameter name will be the same as the 
		/// field or property name.
		/// </summary>
		public string Name { get; set; }

		/// <summary>
		///	Gets or sets the position of the parameter value in the command line,  
		///	relative to other positional parameters.  Named parameters and their values 
		///	are not counted for this purpose.  Positional parameters must be numbered 
		///	starting at 1 (not 0), with no repeats and no skips.  A value of 0 
		///	(the default) means that the parameter cannot be used without its name.
		/// </summary>
		public int Position { get; set; }

		/// <summary>
		/// Gets or sets whether this parameter is required.  
		/// The default is false (not required).
		/// If a positional parameter is required, then so must all the preceding
		/// positional parameters.
		/// </summary>
		public bool Required { get; set; }

		/// <summary>
		/// Gets or sets a help message (documentation) for this parameter.
		/// If the parent CmdLineGroupAttribute has a non-null ResourceNamePrefix,
		/// then ResourceNamePrefix+Help is the name of a resource containing the help.
		/// </summary>
		public string Help;

		/// <summary>
		/// Marks a field or property as being a command-line parameter.  The field 
		/// or property name will be used as the name of the command-line parameter.
		/// </summary>
		public CmdLineArgAttribute ( )
		{
		}

		/// <summary>
		/// Marks a field or property as being a command-line parameter.
		/// </summary>
		/// <param name="paramName">
		///		The name of the command-line parameter.
		///		If null, the field or property name will be used.
		///	</param>
		/// <param name="required">whether the parameter is required</param>
		/// <param name="help">
		///		A help message for the parameter.  If the parent 
		///		CmdLineGroupAttribute.ResourceNamePrefix is non-null, then
		///		ResourceNamePrefix+help is the name of a resource containing the help.
		/// </param>
		public CmdLineArgAttribute (string paramName, bool required, string help)
		{
			this.Name = paramName;
			this.Required = required;
			this.Help = help;
		}

		/// <summary>
		/// Marks a field or property as being a command-line parameter.
		/// </summary>
		/// <param name="position">
		///		The position of the parameter value in the command-line,
		///		or 0 if the parameter value cannot be given without its name.
		/// </param>
		/// <param name="paramName">
		///		The name of the command-line parameter.
		///		If null, the field or property name will be used.
		///	</param>
		/// <param name="required">whether the parameter is required</param>
		/// <param name="help">
		///		A help message for the parameter.  If the parent 
		///		CmdLineGroupAttribute.ResourceNamePrefix is non-null, then
		///		ResourceNamePrefix+help is the name of a resource containing the help.
		/// </param>
		public CmdLineArgAttribute (int position, string paramName, bool required, string help)
		{
			this.Name = paramName;
			this.Required = required;
			this.Help = help;
		}
	}

	//#################################################################################
	/// <summary>
	/// Use this attribute to mark a field or property as being not available from the
	/// command line.  
	/// This is only needed if you set CmdLineGroupAttribute.IncludePublicFieldsByDefault
	/// or .IncludePublicPropertiesByDefault to true.
	/// </summary>

	public class NotAvailableFromCmdLineAttribute : CmdLineMemberAttribute
	{
		/// <summary>
		/// Marks a field or property as being not available from the command line.
		/// </summary>
		public NotAvailableFromCmdLineAttribute () { }
	}

	//#################################################################################
	/// <summary>
	/// Use this attribute to specify a method for parsing a non-standard value type 
	/// for command-line parameters.
	/// </summary>
	/// <remarks>
	/// You must provide your own value parser if you need value parsing for any types
	/// other than: .Net Framework floats, integers (signed and unsigned),
	/// decimal, boolean, string, enums, DateTime, and TimeSpan; also any type that
	/// has a static "Parse" method that takes a single string parameter and returns
	/// the parsed value.
	/// If not specified, the command line parser will examine the type of the 
	/// parameter's field or variable and look for a static Parse(string) method.
	/// </remarks>

	[AttributeUsage(AttributeTargets.Class, AllowMultiple=true, Inherited=false)]
	public class CmdLineValueParserAttribute : Attribute
	{
		/// Gets the type of value to be parsed.
		public readonly Type ValueType;

		/// Gets the name of the parser method.
		public readonly string MethodName;

		/// <summary>
		/// Specifies a method for parsing a non-standard value type for command-line
		/// parameters.  You can provide at most one parser method for each type.
		/// </summary>
		/// <param name="valueType">the value type</param>
		/// <param name="methodName">
		///		The unqualified name of a public static or instance method of the 
		///		attribute's target class, or a fully qualified name of a public static 
		///		method of any class.  The method must take a single string argument
		///		and return the specified type.
		/// </param>
		public CmdLineValueParserAttribute (Type valueType, string methodName)
		{
			Require.NotNull(methodName);
			this.MethodName = methodName;
		}

	}

	//#################################################################################
}
